/*
 *
 * @APPLE_LICENSE_HEADER_START@
 *
 * Copyright (c) 1999-2008 Apple Inc.  All Rights Reserved.
 *
 * This file contains Original Code and/or Modifications of Original Code
 * as defined in and that are subject to the Apple Public Source License
 * Version 2.0 (the 'License'). You may not use this file except in
 * compliance with the License. Please obtain a copy of the License at
 * http://www.opensource.apple.com/apsl/ and read it before using this
 * file.
 *
 * The Original Code and all software distributed under the License are
 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
 * Please see the License for the specific language governing rights and
 * limitations under the License.
 *
 * @APPLE_LICENSE_HEADER_END@
 *
 */
 /*
	 File:       RTPOverbufferWindow.cpp

	 Contains:   Implementation of the class

	 Written By: Denis Serenyi

 */

#include "RTPOverbufferWindow.h"

RTPOverbufferWindow::RTPOverbufferWindow(UInt32 inSendInterval, UInt32 inInitialWindowSize, UInt32 inMaxSendAheadTimeInSec,
	Float32 inOverbufferRate)
	: fWindowSize(inInitialWindowSize),
	fBytesSentSinceLastReport(0),
	fSendInterval(inSendInterval),
	fBytesDuringLastSecond(0),
	fLastSecondStart(-1),
	fBytesDuringPreviousSecond(0),
	fPreviousSecondStart(-1),
	fBytesDuringBucket(0),
	fBucketBegin(0),
	fBucketTimeAhead(0),
	fPreviousBucketTimeAhead(0),
	fMaxSendAheadTime(inMaxSendAheadTimeInSec * 1000),
	fWriteBurstBeginning(false),
	fOverbufferingEnabled(true),
	fOverbufferRate(inOverbufferRate),
	fSendAheadDurationInMsec(1000),
	fOverbufferWindowBegin(-1),
	fPreviousBucketBegin(0)
{
	if (fSendInterval == 0)
	{
		fOverbufferingEnabled = false;
		fSendInterval = 200;
	}

	if (fOverbufferRate < 1.0)
		fOverbufferRate = 1.0;

}

SInt64 RTPOverbufferWindow::CheckTransmitTime(const SInt64& inTransmitTime, const SInt64& inCurrentTime, SInt32 inPacketSize)
{
	// if this is the beginning of a bucket interval, roll over figures from last time.
	// accumulate statistics over the period of a second
	if (inCurrentTime - fBucketBegin > fSendInterval)
	{
		fPreviousBucketBegin = fBucketBegin;
		fBucketBegin = inCurrentTime;
		if (fPreviousBucketBegin == 0)
			fPreviousBucketBegin = fBucketBegin - fSendInterval;
		fBytesDuringBucket = 0;
		if (inCurrentTime - fLastSecondStart > 1000)
		{
			fBytesDuringPreviousSecond = fBytesDuringLastSecond;
			fBytesDuringLastSecond = 0;
			fPreviousSecondStart = fLastSecondStart;
			fLastSecondStart = inCurrentTime;
		}

		fPreviousBucketTimeAhead = fBucketTimeAhead;
	}

	if (fOverbufferWindowBegin == -1)
		fOverbufferWindowBegin = inCurrentTime;

	if ((inTransmitTime <= inCurrentTime + fSendInterval) ||
		(fOverbufferingEnabled && (inTransmitTime <= inCurrentTime + fSendInterval + fSendAheadDurationInMsec)))
	{
		//
		// If this happens, this packet needs to be sent regardless of overbuffering
		return -1;
	}

	if (!fOverbufferingEnabled || (fWindowSize == 0))
		return inTransmitTime;

	// if the client is running low on memory, wait a while for it to be freed up
	// there's nothing magic bout these numbers, we're just trying to be conservative
	if ((fWindowSize != -1) && (inPacketSize * 5 > fWindowSize - fBytesSentSinceLastReport))
	{
		return inCurrentTime + (fSendInterval * 5);  // client reports don't come that often
	}

	// if we're far enough ahead, then wait until it's time to send more packets
	if (inTransmitTime - inCurrentTime > fMaxSendAheadTime)
		return inTransmitTime - fMaxSendAheadTime + fSendInterval;

	// during the first second just send packets normally
//    if (fPreviousSecondStart == -1)
//        return inCurrentTime + fSendInterval;

	// now figure if we want to send this packet during this bucket.  We have two limitations.
	// First we scale up bitrate slowly, so we should only try and send a little more than we
	// sent recently (averaged over a second or two).  However, we always try and send at
	// least the current bitrate and never more than double.
//    SInt32 currentBitRate = fBytesDuringBucket * 1000 / (inCurrentTime - fPreviousBucketBegin);
//  SInt32 averageBitRate = (fBytesDuringPreviousSecond + fBytesDuringLastSecond) * 1000 / (inCurrentTime - fPreviousSecondStart);
//    SInt32 averageBitRate = fBytesDuringPreviousSecond * 1000 / (fLastSecondStart - fPreviousSecondStart);
	fBucketTimeAhead = inTransmitTime - inCurrentTime;
	//	printf("Current br = %d, average br = %d (cta = %qd, pta = %qd)\n", currentBitRate, averageBitRate, currentTimeAhead, fPreviousBucketTimeAhead);

		// always try and stay as far ahead as we were before
	if (fBucketTimeAhead < fPreviousBucketTimeAhead)
		return -1;

	// but don't send at more that double the bitrate (for any given time we should only get further
	// ahead by that amount of time)
	//printf("cta - pta = %qd, ct - pbb = %qd\n", fBucketTimeAhead - fPreviousBucketTimeAhead, SInt64((inCurrentTime - fPreviousBucketBegin) * (fOverbufferRate - 1.0)));
	if (fBucketTimeAhead - fPreviousBucketTimeAhead > ((inCurrentTime - fPreviousBucketBegin) * (fOverbufferRate - 1.0)))
	{
		fBucketTimeAhead = fPreviousBucketTimeAhead + SInt64((inCurrentTime - fPreviousBucketBegin) * (fOverbufferRate - 1.0));
		return inCurrentTime + fSendInterval;		// this will get us to the next bucket
	}

	// don't send more than 10% over the average bitrate for the previous second
//    if (currentBitRate > averageBitRate * 11 / 10)
//        return inCurrentTime + fSendInterval;       // this will get us to the next bucket

	return -1;  // send this packet
}

void RTPOverbufferWindow::ResetOverBufferWindow()
{
	fBytesDuringLastSecond = 0;
	fLastSecondStart = -1;
	fBytesDuringPreviousSecond = 0;
	fPreviousSecondStart = -1;
	fBytesDuringBucket = 0;
	fBucketBegin = 0;
	fBucketTimeAhead = 0;
	fPreviousBucketTimeAhead = 0;
	fOverbufferWindowBegin = -1;
}

void RTPOverbufferWindow::AddPacketToWindow(SInt32 inPacketSize)
{
	fBytesDuringBucket += inPacketSize;
	fBytesDuringLastSecond += inPacketSize;
	fBytesSentSinceLastReport += inPacketSize;
}

void RTPOverbufferWindow::EmptyOutWindow(const SInt64& inCurrentTime)
{
	// no longer needed
}

void RTPOverbufferWindow::SetWindowSize(UInt32 inWindowSizeInBytes)
{
	fWindowSize = inWindowSizeInBytes;
	fBytesSentSinceLastReport = 0;
}
